package org.example.java_gobang.api;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.example.java_gobang.config.WebSocketConfig;
import org.example.java_gobang.game.*;
import org.example.java_gobang.model.User;
import org.example.java_gobang.model.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;

//游戏对战API:
@Component
public class GameAPI extends TextWebSocketHandler {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private OnlineUserManager onlineUserManager = new OnlineUserManager();
    @Autowired
    private RoomManager roomManager;
    //建立连接:
    @Resource
    private UserMapper userMapper;
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //创建一个响应对象:
        GameReadyResponse resp = new GameReadyResponse();
        //1.进行校验工作:
        User user = (User) session.getAttributes().get("user");
        //1.1验证当前用户是否已登录
        if(user==null){
            resp.setOk(false);
            resp.setReason("当前用户尚未登录!");
            //向客户端返回:
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //1.2判断用户是否已经进入到房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            resp.setOk(false);
            resp.setReason("用户尚未进入游戏房间");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //1.3判定用户是否存在多开:
        //若已检测到用户已经进入到游戏大厅或在游戏房间,提醒用户禁止多开:
        if(onlineUserManager.getFromGameHall(user.getUserId())!=null
                || onlineUserManager.getFromGameRoom(user.getUserId())!=null){
            //多开属于正常的逻辑,将结果返回给客户端即可:
            resp.setOk(true);
            resp.setMessage("repeatConnection");
            resp.setReason("检测到当前用户存在多开现象!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //2.判定完成后,进行游戏对战流程:
        // 2.1将当前玩家加入到游戏房间
        onlineUserManager.enterGameRoom(user.getUserId(),session);
        //之前将匹配好的玩家只是放到了房间管理器中,还未放到创建的房间中,
        //真正将玩家加入到游戏方房间中,这个过程中涉及页面跳转,从game_hall进入到game_room界面,
        // 这是一个很大的步骤,很容易出现问题,因此 当跳转成功后,再将玩家信息存入到游戏房间中.
        //此时可能存在多线程问题,当多个玩家同时进入房间时,就无法确定谁是1号玩家了,
        //此时是同一个房间内可能存在多线程问题,对于不同的房间,玩家之间是不受影响的,
        // 因此要对 当前房间 上锁,每次只能一个玩家操作该房间
        synchronized(room){
            // 2.1.1当游戏房间为空的时候,将当前玩家设为房间中的第一个玩家
            if(room.getUser1()==null){
                room.setUser1(user);
                // 把先进入房间的玩家作为先手方,白棋玩家
                room.setWhiteUser(user.getUserId());
                System.out.println("玩家 "+user.getUsername()+" ,作为玩家1,已准备就绪!");
                return;
            }
            // 2.1.2此时,说明玩家1已经有人了,只能将当前用户以玩家2的位置存入
            if(room.getUser2()==null){
                room.setUser2(user);
                System.out.println("玩家 "+user.getUsername()+" ,作为玩家2,已准备就绪!");

                // 2.1.3 此时游戏房间中得分人数已经够了,让服务器给两个玩家都返回相关信息,
                //通知玩家,游戏准备就绪
                //通知第一个先手玩家:
                noticeGameReady(room,room.getUser1(),room.getUser2());
                //通知第二个后手玩家:
                noticeGameReady(room,room.getUser2(),room.getUser1());
                return;
            }
        }
        // 3.此时若又有玩家尝试连接同一个已经满员的房间,提示报错
        //这种情况理论上是不存在的
        resp.setOk(false);
        resp.setReason("当前房间已满,无法加入该房间!");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    //  通知玩家,游戏准备就绪
    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse resp = new GameReadyResponse();
        resp.setOk(true);
        resp.setMessage("gameReady");
        resp.setReason("");
        resp.setThisUserId(thisUser.getUserId());
        resp.setThatUserId(thatUser.getUserId());
        resp.setWhiteUser(room.getWhiteUser());
        //将当前数据传给玩家
        WebSocketSession session = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
       //1.获取当前用户信息
        User user = (User) session.getAttributes().get("user");
        if(user==null){
            System.out.println("[handleTextMessage] 当前玩家尚未登录!");
            return;
        }
        //2. 获取房间信息
        Room room = roomManager.getRoomByUserId(user.getUserId());
        //对房间中的信息做出处理
        room.putChess(message.getPayload());

    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user==null){
            //当用户为空时,直接返回,不再做出处理
            return;
        }
        WebSocketSession tmpUser = onlineUserManager.getFromGameRoom(user.getUserId());
        //防止因为多开导致的正常用户下线:
        if(session==tmpUser){
            //退出游戏房间:
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前玩家: "+user.getUsername()+" 游戏房间连接异常 ");
        //当一方因为异常断开了游戏连接后,通知另一方游戏获胜:
        noticeThatUserWin(user);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user==null){
            //当用户为空时,直接返回,不再做出处理
            return;
        }
        WebSocketSession tmpUser = onlineUserManager.getFromGameRoom(user.getUserId());
        //防止因为多开导致的正常用户下线:
        if(session==tmpUser){
            //退出游戏房间:
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        System.out.println("当前玩家: "+user.getUsername()+" 离开游戏房间");
        //当一方因为异常断开了游戏连接后,通知另一方游戏获胜:
        noticeThatUserWin(user);
    }

    //当一方突然关闭网页等, 因为异常断开了游戏连接后,通知另一方游戏获胜:
    private void noticeThatUserWin(User user) throws IOException {
        // 1. 根据当前玩家,找到所在的游戏房间:
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room==null){
            //说明当前房间已经被释放:
            return;
        }
        //找到对手玩家:
        User thatUser = user==room.getUser1()?room.getUser2():room.getUser1();
        WebSocketSession session = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if(session==null){
            //若对手玩家也下线了,就不再处理了:
            return;
        }
        //将信息返回:
        GameResponse resp = new GameResponse();
        resp.setMessage("putChess");
        resp.setWinner(thatUser.getUserId());
        resp.setUserId(thatUser.getUserId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
        //游戏结束,修改两个玩家的信息
        userMapper.userWin(thatUser.getUserId());
        userMapper.userLose(user.getUserId());
        //销毁游戏房间
        roomManager.remove(room.getRoomId(),user.getUserId(),thatUser.getUserId());
    }
}
